#-------------------------------------------------------------------------------
# Name:        ShortCircuit_ZhuyeZinc
# Purpose:
#
# Author:      Daiyusi
#
# Created:     20/11/2021
# Copyright:   (c) asus 2021
# Licence:     <your licence>
#-------------------------------------------------------------------------------

import numpy as np
from PIL import Image,ImageFont,ImageDraw
import math
from numpy.linalg import *
# import matplotlib.pyplot as plt

import pickle

class CropDetc2:
    def avg_sum(self, im):
        # 面均值
        s = np.float(0)
        h, w = im.shape
        for i in range(h):
            for j in range(w):
                s = s * ((i * w + j) / (i * w + j + 1)) + im[i, j] / (i * w + j + 1)
        return s

    def max_avg_sum(self, im):
        # 线均值最大值
        h, w = im.shape
        maxs = 0
        for i in range(w):
            s = np.float(0)
            for j in range(h):
                s = s * (j / (j + 1)) + im[j, i] / (j + 1)
            if s > maxs:
                maxs = s
        return maxs

    def max2_avg_sum(self, im):
        # 半线均值最大值
        h, w = im.shape
        h2 = int(h / 2)
        maxs1 = self.max_avg_sum(im[0:h2, 0:])
        maxs2 = self.max_avg_sum(im[h2:, 0:])

        if maxs1 > maxs2:
            return maxs1
        else:
            return maxs2
        '''
        s = (maxs1+maxs2)/2
        return s
        '''

    def max3_avg_sum(self, im):
        # (半)线均值最大值与面均值的加权平均
        s = np.float(0)
        s0 = self.avg_sum(im)
        s1 = self.max_avg_sum(im)
        s2 = self.max2_avg_sum(im)
        s = 0.7 * s2 + 0.3 * s0
        return s

    def uncovSearch(self, avgT, meanT):
        # 头尾板搜索计数,以p倍meanT为界
        n = [1, 1]  # [头,尾]
        p = 1.20

        for i in avgT[1:]:
            if i > p * meanT:
                n[0] = n[0] + 1
            else:
                if n[0] == 1 and avgT[0] < p * meanT:
                    n[0] = 0
                break

        for i in avgT[-2::-1]:
            if i > p * meanT:
                n[1] = n[1] + 1
            else:
                if n[1] == 1 and avgT[-1] < p * meanT:
                    n[1] = 0
                break

        # print('n:', n)
        return n

    def T2P(self, T, minT, maxT):
        # P = (T-minT)*255/(maxT-minT)
        k = 255 / (maxT - minT)
        P = []

        if type(T) is list:
            for i in T:
                p0 = (i - minT) * k
                P.append(p0)
        else:
            P = (T - minT) * k

        return P

    def thDynamic(self, th0, minT, maxT):
        th=[]
        for i in th0:
            th.append(i)
        return th


    def stdDetect(self, im, minT, maxT, num):
        # 单槽板极短路检测
        levelSC = self.levelSC

        diag = [0 for i in range(num)]
        avgT = [0 for i in range(num)]
        h, w = im.shape
        stp = w / num

        # 极板特征值列表求取
        for i in range(num):
            p1 = int(i * stp)
            p2 = int((i + 1) * stp)
            subim = im[0:, p1:p2]
            avgT[i] = self.max3_avg_sum(subim)
            # print('avgT[', str(i + 1), ']:', avgT[i])

        ## List Of avgT
        self.ListExtr_avg.append(avgT)

        meanT = np.mean(avgT)  # 均值
        stdT = np.std(avgT)  # 标准差
        #    print('meanT1:',meanT)
        difT = avgT - meanT
        uncov = [1 for i in range(num)]  # 暗区标记0
        num_uncov = num
        sum_uncov = 0
        threcov = 2
        thuncov = - threcov*stdT
        for i in range(num):
            if difT[i] < thuncov:
                num_uncov = num_uncov - 1
                uncov[i] = 0
            sum_uncov = sum_uncov + uncov[i] * avgT[i]

        meanT = sum_uncov / num_uncov
        # print('meanT:', meanT)

        # 前后端n个板极忽略，不纳入均值计算

        #T1 = 66.5  # 盖布与不盖布的均温划分界限
        #P1 = self.T2P(T1, minT, maxT)
        #P1 = 180
        n = [0, 0]
        # print('meanT1:',meanT)
        # if meanT < P1:
        #     n = self.uncovSearch(avgT, meanT)
        #     for i in range(-n[1], n[0]):
        #         #n.append(avgT[i])
        #         if uncov[i] == 1:
        #             # uncov[i] = 0
        #             num_uncov = num_uncov - 1
        #             sum_uncov = sum_uncov - avgT[i]
        #     meanT = sum_uncov / num_uncov

        self.ListExtr_ns.append(n) # List Of n
        difT = avgT - meanT

        # 忽略前后n个板极，以及多层盖布后求标准差
        avgT_normal = []
        for i in range(n[0], num - n[1]):
            if uncov[i] == 1:
                avgT_normal.append(avgT[i])
        stdT = np.std(avgT_normal)

        # print('meanT2:', meanT)
        # print('stdT:', stdT)
        # print('avgT:', avgT)
        # print('difT:', difT)

        thrSTD = [0.09, 0.12]
        # thAVG = [160,180,200]
        # k3, k2, k1 = [3.5, 2.5, 2.1]
        # k3, k2, k1 = [5, 3.6, 2.8]
        k3, k2, k1 = self.parmList
        # 标准差过大
        if stdT > thrSTD[0] * meanT:
            k3, k2, k1 = [4.5, 2.8, 2.1]
        if stdT > thrSTD[1] * meanT:
            k3, k2, k1 = [4.5, 2.5, 2.0]

        for i in range(n[0], num - n[1]):
            if uncov[i] == 1:
                if difT[i] > k3 * stdT:
                    diag[i] = levelSC[3]
##                    if avgT[i] < thAVG[2]:
##                        diag[i] = 2
                elif difT[i] > k2 * stdT:
                    diag[i] = levelSC[2]
##                    if meanT < thAVG[1] and avgT[i] > thAVG[2]:
##                        diag[i] = 2
                elif difT[i] > k1 * stdT:
                    diag[i] = levelSC[1]
##                elif meanT < thAVG[1] and avgT[i] > thAVG[2]:
##                    diag[i] = levelSC[1]

##                if avgT[i] < thAVG[0]:
##                    diag[i] = levelSC[0]

        # 对最前、后n块板极中未被多层盖布遮挡的板极单独做阈值检测
        #thT = [120, 140, 160]
        #th = self.T2P(thT, minT, maxT)
        '''
        th0 = [180,190,200]
        th = self.thDynamic(th0, minT, maxT)
        '''
        th = [135,145,160] # 两端板极检测阈值
        for i in range(-n[1], n[0]):
            if uncov[i] == 1:
                if avgT[i] > th[2]:
                    diag[i] = levelSC[3]
                elif avgT[i] > th[1]:
                    diag[i] = levelSC[2]
                elif avgT[i] > th[0]:
                    diag[i] = levelSC[1]

        return diag

    def DistorRemove(self, im, k1, k2):
        # 桶形畸变矫正
        s = im.shape
        img = np.zeros(s)
        img = np.uint8(img)

        for l1 in range(s[0]):
            y = l1 - s[0] / 2

            for l2 in range(s[1]):
                x = l2 - s[1] / 2
                x1 = np.round(x * (1 + k1 * x * x + k2 * y * y))
                y1 = np.round(y * (1 + k1 * x * x + k2 * y * y))
                x1 = np.int(x1 + s[1] / 2)
                y1 = np.int(y1 + s[0] / 2)

                # 超出边界部分强制为0
                if x1 < 0 and x1 >= s[1] and y1 < 0 and y1 >= s[0]:
                    img[l1, l2] = 0;
                else:
                    img[l1, l2] = im[y1, x1]
        return img

    def DistorRemove2_toushijibian(self, im, dot):
        s = im.shape
        M = s[0]
        N = s[1]

        w = round(math.sqrt(pow((dot[0][0]-dot[1][0]),2)+pow((dot[0][1]-dot[1][1]),2)))
        h = round(math.sqrt(pow((dot[0][0]-dot[2][0]),2)+pow((dot[0][1]-dot[2][1]),2)))

        y = [dot[0][0],dot[1][0],dot[2][0],dot[3][0]]
        x = [dot[0][1],dot[1][1],dot[2][1],dot[3][1]]

        Y = [dot[0][0],dot[0][0],dot[0][0]+h,dot[0][0]+h]
        X = [dot[0][1],dot[0][1]+w,dot[0][1],dot[0][1]+w]

        B = [X[0],Y[0],X[1],Y[1],X[2],Y[2],X[3],Y[3]]
        A = [[x[0],y[0],1,0,0,0,-X[0]*x[0],-X[0]*y[0]],[0,0,0,x[0],y[0],1,-Y[0]*x[0],-Y[0]*y[0]],[x[1],y[1],1,0,0,0,-X[1]*x[1],-X[1]*y[1]],[0,0,0,x[1],y[1],1,-Y[1]*x[1],-Y[1]*y[1]],[x[2],y[2],1,0,0,0,-X[2]*x[2],-X[2]*y[2]],[0,0,0,x[2],y[2],1,-Y[2]*x[2],-Y[2]*y[2]],[x[3],y[3],1,0,0,0,-X[3]*x[3],-X[3]*y[3]],[0,0,0,x[3],y[3],1,-Y[3]*x[3],-Y[3]*y[3]]]
        B = np.array(B).T
        A = np.array(A)

        fa = inv(A) @ B
        a = fa[0]
        b = fa[1]
        c = fa[2]
        d = fa[3]
        e = fa[4]
        f = fa[5]
        g = fa[6]
        h = fa[7]

        rot = np.array([[d,e,f],[a,b,c],[g,h,1]])

        pix1 = rot @ (np.array([1,1,1]).T) / (g*1 + h*1 + 1)
        pix2 = rot @ (np.array([1,N,1]).T) / (g*1 + h*N + 1)
        pix3 = rot @ (np.array([M,1,1]).T) / (g*M + h*1 + 1)
        pix4 = rot @ (np.array([M,N,1]).T) / (g*M + h*N + 1)

        height = round(max(pix1[0],pix2[0],pix3[0],pix4[0]) - min(pix1[0],pix2[0],pix3[0],pix4[0]))
        width = round(max(pix1[1],pix2[1],pix3[1],pix4[1]) - min(pix1[1],pix2[1],pix3[1],pix4[1]))
        img = np.zeros([height,width])
        img_index = np.zeros([height,width,2])
        # print(img_index)

        delta_y = round(abs(min(pix1[0],pix2[0],pix3[0],pix4[0])))
        delta_x = round(abs(min(pix1[1],pix2[1],pix3[1],pix4[1])))
        inv_rot = inv(rot)

        for i in range(1-delta_y,height-delta_y+1):
            for j in range(1-delta_x,width-delta_x+1):
                pix = inv_rot @ (np.array([i,j,1]).T)
                pix = inv(np.array([[g*pix[0]-1,h*pix[0]],[g*pix[1],h*pix[1]-1]])) @ (np.array([-pix[0],-pix[1]]).T)
                pix = pix - 1
                if pix[0] >= 0 and pix[1] >= 0 and pix[0] <= M-1 and pix[1] <= N-1:
                    img[i+delta_y-1,j+delta_x-1] = im[round(pix[0]),round(pix[1])]  #最近邻插值（也可以用双线性或双立方插值）

                    # print([round(pix[0]),round(pix[1])])
                    img_index[i+delta_y-1,j+delta_x-1] = list([round(pix[0]),round(pix[1])])
                    # print(img_index[i+delta_y-1,j+delta_x-1])

        # 保存x
        # print(img_index,'//shape:',img_index.shape)

        f = open('img_index.pckl', 'wb')
        pickle.dump(img_index, f)
        f.close()

        return img

    def DistortionRemove2_toushijibian_load(self, im):
        # 加载x
        f = open('C:/RealPlayZinc/SCDDemo/img_index.pckl', 'rb')
        img_index = pickle.load(f)
        f.close()

        s = np.array(img_index).shape
        img = np.zeros([s[0],s[1]])
        # print(img_index)
        for i in range(s[0]):
            for j in range(s[1]):
                idx = list(img_index[i,j])
                # print(idx)
                img[i,j] = im[round(idx[0]),round(idx[1])]

        return img

    def CropOfBars(im):
        # 单槽自动裁剪（待完善）
        pass

    def DecInit(self):
        # 生成key为‘槽号-板极号’，值为0的初始化字典
        diag = {}
        ii = 0
        for i in range(self.num1[0], self.num1[1] + 1):
            for j in range(self.num2[ii][0], self.num2[ii][1] + 1):
                key = str(i) + '-' + str(j)
                diag[key] = [0 for i in range(self.num3[ii])];
            ii = ii + 1
        return diag

    def stdDetect2(self,n1,n2):
        # 所有头、尾板另行检测
        levelSC = self.levelSC
        Ln = self.ListExtr_ns
        Lavg = []
        Lavg2 = []
        Ln2 = []

        k = n1*n2

        # 忽略边缘
        '''
        for i in range(k):
            j = Ln[i][0]
            if j > 0:
                Ln[i][0] = Ln[i][0]-1

            j = Ln[i][1]
            if j > 0:
                Ln[i][1] = Ln[i][1]-1
        '''

        for i in range(k):
            if Ln[i] == [0,0]:
                Lavg.append([])
            elif Ln[i][1] == 0:
                Lavg.append(self.ListExtr_avg[i][0:Ln[i][0]])
            elif Ln[i][0] == 0:
                Lavg.append(self.ListExtr_avg[i][-Ln[i][1]::])
            else:
                Lavg.append(self.ListExtr_avg[i][0:Ln[i][0]]+self.ListExtr_avg[i][-Ln[i][1]::])

            Lavg2 = Lavg2 + Lavg[i]

        mExtr = np.mean(Lavg2)
        sExtr = np.std(Lavg2)

        # print('mExtr:',mExtr)
        # print('sExtr:',sExtr)
        # print('Lavg2:',Lavg2)

        warncof = [0.5, 1, 2]
        #warnadd = [10,20,30]

        warnLine = [mExtr + i*sExtr for i in warncof]
        #warnLine = [mExtr + i  for i in warnadd]

        diag = [[0 for i in range(self.num3)] for j in range(k)]

        for i in range(k):
            n = Ln[i]
            for j in range(-n[1],n[0]):
                x = Lavg[i][j]
                if x > warnLine[2]:
                    diag[i][j] = levelSC[3]
                elif x > warnLine[1]:
                    diag[i][j] = levelSC[2]
                elif x > warnLine[0]:
                    diag[i][j] = levelSC[1]

        return diag

    def ShortCircuitDetect(self, im, dot, minT=30, maxT=90):
        # 短路检测主程序
        im = im.convert('L')  # 转灰度图
        im = np.array(im)  # 图片转数组形式存储

        # 生成初始化字典,全0
        diag = self.DecInit()
        diag1 = []

        # 桶形畸变矫正
        # k1 = -3e-7;
        # k2 = -4e-7;
        # img = self.DistorRemove(im, k1, k2)

        #img = self.DistorRemove2_toushijibian(im, dot)
        img = self.DistortionRemove2_toushijibian_load(im)

        # plt.imshow(img, cmap='gray', interpolation='bicubic')
        # plt.show()

        # 手动定位裁剪
        q = [[[115,187,505,265],[96,310,505,392],[78,434,501,529],[76,568,498,667],[67,686,492,808],[69,829,487,946],[69,973,487,1091],[73,1136,484,1193],[82,1264,489,1315]],[[763,308,1036,365],[770,421,1045,532],[767,550,1048,668],[766,692,1051,809],[764,833,1051,950],[763,973,1037,1078],[758,1109,1025,1184]]]

        # 所有单槽中的板极短路检测
        n1 = self.num1[1] - self.num1[0] + 1
        n2 = 0
        k0 = 0

        for i in range(n1):
            # 起始槽编号
            k0 = k0 + n2
            # 上、下整槽分离
            # 单槽分离及其板极短路检测
            n2 = self.num2[i][1] - self.num2[i][0] + 1
            n3 = self.num3[i]

            n10 = self.num1[0]
            n20 = self.num2[i][0]

            for j in range(n2):
                k = k0 + j

                im1bar = img[(q[i][j][0] - 1):q[i][j][2],(q[i][j][1] - 1):q[i][j][3]]
                # im1bar = np.resize(im1bar,[num3*10,100])

                # plt.imshow(im1bar,cmap='gray',interpolation='bicubic')
                # plt.show()

                diag_curr = self.stdDetect(im1bar.T, minT, maxT, n3)
                diag1.append(diag_curr)  # 输入图片中板极纵向排列，0:正常 1:轻度 2:中度 3:重度

                key = str(n10 + i) + '-' + str(n20 + j)
                diag[key] = diag_curr

                # plt.figure()
                # # plt.subplot(2, 1, 1)
                # plt.imshow(im1bar.T, cmap='gray', interpolation='bicubic')
                # # plt.subplot(2, 1, 2)
                # # plt.axis([1, self.num3, 0, 3])
                # # plt.plot(list(range(1, num3 + 1)), diagBars, marker='*')
                # plt.savefig('SDC_' + str(i + 1) + '-' + str(j + 1) + '.jpg', dpi=120)
                # plt.show()

        # print('diag:',diag)

        return diag

    def MyCheck(self, path, dot, parmList, maxT = 90, minT = 30):
        imPath = path  # 'E:\\PythonProject\\ShortCircuitDetection\\Test003.jpg'
        # print(imPath)
        img = Image.open(imPath)  # read the image ( PIL image )
        self.parmList = parmList
        diag = self.ShortCircuitDetect(img, dot, minT, maxT)  # 短路检测
        return diag

    def __init__(self):
        self.parmList = [4.5, 3.5, 2.5]
        self.num1 = [1, 2]  # 起始-结束整槽号
        self.num2 = [[7,15], [8,14]]  # 起始-结束单槽号
        self.num3 = [60,40]  # number of plates in one electrobath
        self.levelSC = [0, 1, 2, 3] # 0:正常, 1:疑似, 2:一般, 3:严重

        self.ListExtr_ns = []
        self.ListExtr_avg = []

